% Created 2022-05-26 周四 00:31
% Intended LaTeX compiler: xelatex
\documentclass[a4paper]{article}
                 \usepackage{graphicx}
                 \usepackage{xcolor}
                 \usepackage{xeCJK}
                 \usepackage{fixltx2e}
                 \usepackage{longtable}
                 \usepackage{float}        
                 \usepackage{tikz}         
                 \usepackage{wrapfig}      
                 \usepackage{latexsym,amssymb,amsmath}
                 \usepackage{textcomp}
                 \usepackage{bicaption}
                 \usepackage{listings}     
                \usepackage{marvosym}     
                \usepackage{textcomp}     
                \usepackage{latexsym}     
                \usepackage{natbib}
                \usepackage{indentfirst}
		\usepackage{hyperref}
                \usepackage{geometry}
                \usepackage{minted}
                \setCJKmainfont{宋体}
                \setlength{\parindent}{2em}
                \usepackage{color}
                \usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead[R]{\footnotesize{《Android 物联网应用程序开发》 广西职业师范学院实验教学指导书}}
\geometry{a4paper,left=2.5cm,right=2.5cm,top=2cm,bottom=2cm}
\setCJKmainfont{仿宋}
\setCJKsansfont{Microsoft YaHei}
\setCJKmonofont{Microsoft YaHei}

            \lstset{
  columns=fixed,       
  breaklines=true, breakautoindent=true,breakindent=4em,
  numbers=left,                                        % 在左侧显示行号
  frame=none,                                          % 不显示背景边框
  backgroundcolor=\color[RGB]{245,245,244},            % 设定背景颜色
  keywordstyle=\color[RGB]{40,40,255},                 % 设定关键字颜色
  numberstyle=\footnotesize\color{darkgray},           % 设定行号格式
  commentstyle=\it\color[RGB]{0,96,96},                % 设置代码注释的格式
  stringstyle=\rmfamily\slshape\color[RGB]{128,0,0},   % 设置字符串格式
  showstringspaces=false,                              % 不显示字符串中的空格
  language=java,                                        % 设置语言
}

\usemintedstyle{emacs}
    \setminted{%
        autogobble,% automatically remove the spaces to the left of the code
        linenos=true,% show line numbers
        fontsize=\small,% smaller font size
        % escapeinside=@@,% insert latex code between @ and @
        frame=lines,% frame styles: none or lines
        % frame=none,
        numbers=none,% show line numbers on: left, right, both or none
        mathescape=true,% insert math code
	breaklines=true,
    }%
    \setmintedinline{%
        fontsize=\smallsize,
    }%
\date{\today}
\title{实验十二: 多媒体实训}
\hypersetup{
 pdfauthor={},
 pdftitle={实验十二: 多媒体实训},
 pdfkeywords={},
 pdfsubject={},
 pdfcreator={Emacs 27.1 (Org mode 9.3.8)}, 
 pdflang={English}}
\begin{document}

\maketitle
\tableofcontents

\newpage

\section{实验目的}
\label{sec:org81aec8b}
\begin{itemize}
\item 掌握 Android 多媒体与其他组件配合实现复杂应用的相关技术
\end{itemize}
\section{实验设备和器件}
\label{sec:org389a519}
\begin{itemize}
\item PC
\item 内存要求 8G 以上
\end{itemize}
\section{课堂实训一:简单音乐播放实训}
\label{sec:org1abaa2a}
\subsection{实训步骤}
\label{sec:orgce79c5e}
\begin{itemize}
\item 在AS中新建项目，名称为SimpleMusicPlay
\item 实现本项目的一个Activity和对应的布局文件，即SimpleMusicPlay.java 和 activity\_main.xml,添加一个按钮play，并添加监听
\item 在网上找一首你喜欢的音乐(mp3格式), 举个例子，比如我下载的歌曲名为sleepaway.mp3,复制到项目res/raw文件夹下
\item 在play按钮的监听中添加如下代码:
\end{itemize}
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
MediaPlayer player = MediaPlayer.create(SimpleMusicPlay.this,R.raw.sleepaway);
player.start();
\end{lstlisting}
\begin{itemize}
\item 运行程序，并将系统音量调大，看有没有音乐,没有音响设备的电脑可以编译成apk传到自己的手机上听。
\item 思考:如何停止或暂停已经播放的音乐?
\end{itemize}

\section{课堂实训二:使用Service后台播放音乐}
\label{sec:org0de1fed}
\subsection{实训步骤}
\label{sec:orgeae16f2}
\begin{itemize}
\item 新建MusicActivity.java,并按照要求创建具体的布局组件
\begin{center}
\includegraphics[width=.9\linewidth]{img/2.JPG}
\end{center}
\item 在MusicActivity.java添加组件
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
private Button startButton;   
private Button pauseButton;
private SeekBar seekbar;

private boolean isBound = false;
private PlayService playService;
private static final String TAG = "MusicActivity";
private final int MUSICDURATION = 0x1;    //获取歌曲播放时间标志
private final int UPDATE = 0x2;       //更新进度条标志
\end{lstlisting}

\item 让MusicActivity实现View.OnClickListener,SeekBar.OnSeekBarChangeListener两个接口，这样该Activity同时也变成了监听类
\item 创建后台PlayService.java,并让它继承Service类，实现MediaPlayer.OnCompletionListener接口，并添加成员MediaPlayer mediaPlayer;
\item 在PlayService类添加几个方法以控制MediaPlayer的状态转移  
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
public void play() {
    try {
	mediaPlayer.reset();
	mediaPlayer.prepare();
	mediaPlayer.start();
    } catch (IOException e) {
	e.printStackTrace();
    }
}

public void pause() {
    mediaPlayer.pause();
    isPlaying = false;
}

public void start() {
    mediaPlayer.start();
    isPlaying = true;
}

public void setTo(int msec) {
    mediaPlayer.seekTo(msec);
}

public int getDuration() {
    return duration;
}

public MediaPlayer getMediaPlayer() {
    return mediaPlayer;
}

@Override
public void onCompletion(MediaPlayer mp) {
    isPlaying = false;
}
\end{lstlisting}
\item 重构onBind方法，并添加一个Binder内部类能够在外部获取Service的实例
\end{itemize}
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
@Override
 public IBinder onBind(Intent intent) {
     return new PlayBinder();
 }

 class PlayBinder extends Binder {
     public PlayService getPlayService() {
	 return PlayService.this;
     }
 }
\end{lstlisting}
\begin{itemize}
\item 其他方法一览
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
    @Override
public void onCreate() {
    super.onCreate();
    try {
	mediaPlayer = MediaPlayer.create(this, R.raw.sleepaway);
	mediaPlayer.setOnCompletionListener(this);
	duration = mediaPlayer.getDuration();
    } catch (Exception e) {
	e.printStackTrace();
    }
}

private Runnable runnable = new Runnable() {
    @Override
    public void run() {
	mediaPlayer.seekTo(mediaPlayer.getCurrentPosition());
    }
};
\end{lstlisting}
\item 将MusicActivity与PlayService进行整合
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
 //连接Activity和Service
private ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
	PlayService.PlayBinder playBinder = (PlayService.PlayBinder) service;
	playService = playBinder.getPlayService();
	handler.sendEmptyMessage(MUSICDURATION);
	//timer.schedule(timertask, 0, 500);
	//executeservice.execute(updateRunnable);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
	playService = null;
    }
};

//绑定服务
private void bindPlayService() {

    if (isBound == false) {
	Intent intent = new Intent(MusicActivity.this, PlayService.class);
	bindService(intent, conn, Context.BIND_AUTO_CREATE);
	isBound = true;
    }
}

//解绑服务
private void unbindPlayService() {
    if (isBound == true) {
	unbindService(conn);
	isBound = false;
    }
}
\end{lstlisting}
\item 使用Handler的方式根据后台服务的进度更新前端UI-Seekbar组件的进度
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
private Handler handler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
	super.handleMessage(msg);
	switch (msg.what) {
	    case MUSICDURATION:
		seekbar.setMax(playService.getDuration());
		break;
	    case UPDATE:
		try {
		    seekbar.setProgress(playService.getMediaPlayer().getCurrentPosition());
		} catch (Exception e) {
		    e.printStackTrace();
		}
		//handler.sendEmptyMessageDelayed(UPDATE);
		handler.sendEmptyMessageDelayed(UPDATE,500);
		break;
	}
    }
};
\end{lstlisting}

\begin{itemize}
\item MusicActivity的onCreate方法一览
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_music);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    init();
    bindPlayService();


    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
	@Override
	public void onClick(View view) {
	    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
		    .setAction("Action", null).show();
	}
    });
}
\end{lstlisting}
\end{itemize}
\begin{itemize}
\item 组件初始化方法init()
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
private void init() {
	pauseButton = (Button) findViewById(R.id.pause);
    startButton = (Button) findViewById(R.id.play);
    seekbar = (SeekBar) findViewById(R.id.seekBar);
    seekbar.setProgress(0);

    pauseButton.setOnClickListener(this);
    startButton.setOnClickListener(this);
    seekbar.setOnSeekBarChangeListener(this);
}
\end{lstlisting}
\item MusicActivity其他方法一览
\lstset{language=java,label= ,caption= ,captionpos=b,numbers=none}
\begin{lstlisting}
  @Override
protected void onPause() {
    super.onPause();
    unbindPlayService();
}


private Runnable updateRunnable = new Runnable() {
    @Override
    public void run() {
	while (true) {

	    try {
		if (playService.getMediaPlayer() != null && playService.isPlaying == true) {
		    handler.sendEmptyMessage(UPDATE);
		}
		Thread.sleep(500);
	    } catch (InterruptedException e) {
		// TODO: handle exception
	    }
	}

    }
};

@Override
public void onClick(View v) {
    switch (v.getId()) {
	case R.id.pause:
	    playService.pause();
	    break;
	case R.id.play:
	    playService.start();
	    handler.sendEmptyMessage(UPDATE);  //发送Message
	    break;
	default:
	    break;
    }
}

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    if (fromUser) {
	Toast.makeText(MusicActivity.this, "" + formatTimeFromProgress(progress), Toast.LENGTH_SHORT).show();
	playService.setTo(progress);
    }
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {

}

/**
 * 将播放进度的毫秒数转换成时间格式 如 3000 --> 00:03
 *
 * @param progress
 * @return
 */
private String formatTimeFromProgress(int progress) {
    // 总的秒数
    int msecTotal = progress / 1000;
    int min = msecTotal / 60;
    int msec = msecTotal % 60;
    String minStr = min < 10 ? "0" + min : "" + min;
    String msecStr = msec < 10 ? "0" + msec : "" + msec;
    return minStr + ":" + msecStr;
}

\end{lstlisting}
\end{itemize}
\end{itemize}
\end{document}